package tools

import (
	"errors"
	"reflect"
	"strings"
	"unicode"
)

func IndirectReflectType(obj any) reflect.Type {
	switch o := obj.(type) {
	case reflect.Type:
		return ReflectTypeIndirect(o)
	case reflect.Value:
		panic(errors.New("IndirectReflectType: 不支持reflect.Value类型"))
	default:
		return ReflectTypeIndirect(reflect.TypeOf(obj))
	}
}

func ReflectTypeIndirect(refType reflect.Type) reflect.Type {
	if refType.Kind() == reflect.Ptr {
		refType = refType.Elem()
	}
	return refType
}

func GetBeanName(name string) string {
	nameSplit := strings.Split(name, ".")
	return FirstLetter2Lower(nameSplit[len(nameSplit)-1])
}

func FirstLetter2Lower(str string) string {
	sLen := len(str)
	if sLen == 0 {
		return ""
	}
	newSli := make([]byte, 0, sLen)
	firstChar := str[0]
	if isUpper := firstChar >= 'A' && firstChar <= 'Z'; isUpper {
		firstChar += 'a' - 'A'
		newSli = append(newSli, firstChar)
	}
	if sLen > 1 {
		newSli = append(newSli, str[1:]...)
	}
	return string(newSli)
}

// FirstLetter2Upper 首字母大写
func FirstLetter2Upper(str string) string {
	sLen := len(str)
	if sLen == 0 {
		return ""
	}
	newSli := make([]byte, 0, sLen)
	firstChar := str[0]
	if isLower := firstChar >= 'a' && firstChar <= 'z'; isLower {
		firstChar -= 'a' - 'A'
		newSli = append(newSli, firstChar)
	}
	if sLen > 1 {
		newSli = append(newSli, str[1:]...)
	}
	return string(newSli)
}

func FindAnnotationValue(bean, annotationType any, tagName string) (string, bool) {
	Assert().NotNil(bean, "bean must not be nil")
	rtBean := reflect.Indirect(reflect.ValueOf(bean)).Type()
	return FindAnnotationValueByType(rtBean, annotationType, tagName)
}

func FindAnnotationValueByType(refType reflect.Type, annotationType any, tagName string) (string, bool) {
	Assert().NotNil(refType, "refType must not be nil")
	refType = IndirectReflectType(refType)
	rtAnnotation := IndirectReflectType(annotationType)
	for i := 0; i < refType.NumField(); i++ {
		structField := refType.Field(i)
		if structField.Type == rtAnnotation {
			return structField.Tag.Lookup(tagName)
		}
	}
	return "", false
}

func SpitNotLetterAndNumber(str string) []string {
	return strings.FieldsFunc(str, func(c rune) bool {
		// 返回true：表示分割，false：表示保留
		// 表示非字母和数字需要分割
		return !unicode.IsLetter(c) && !unicode.IsNumber(c)
	})
}

func GetFullPathNameByRt(rt reflect.Type) string {
	if rt.Kind() == reflect.Ptr {
		rt = rt.Elem()
	}
	return strings.Join([]string{rt.PkgPath(), rt.Name()}, ".")
}

func GetFullPathName(tp any) string {
	rt := IndirectReflectType(tp)
	return GetFullPathNameByRt(rt)
}

func Contains[T comparable](list []T, ele T) bool {
	for _, s := range list {
		if s == ele {
			return true
		}
	}
	return false
}

func Assignable(objPtr any, assignedType reflect.Type) bool {
	rtPtr := reflect.TypeOf(objPtr)
	return Assignable4Rt(rtPtr, assignedType)
}

func Assignable4Rt(rtPtr reflect.Type, assignedType reflect.Type) bool {
	// 如果被赋值类型为结构体，则转换为结构体指针类型
	if assignedType.Kind() == reflect.Struct {
		assignedType = reflect.PointerTo(assignedType)
	}

	// 如果 assignedType 是接口的指针类型，则获取指针指向的接口类型
	if assignedType.Kind() == reflect.Ptr && assignedType.Elem().Kind() == reflect.Interface {
		assignedType = assignedType.Elem()
	}

	// AssignableTo 包含了指针implements()方法
	if rtPtr.AssignableTo(assignedType) {
		return true
	}

	return assignedType.Kind() == reflect.Interface && ReflectTypeIndirect(rtPtr).Implements(assignedType)
}

func RemoveFromSlice[T comparable](slicePtr *[]T, element T) {
	if slicePtr != nil {
		slice := *slicePtr
		index := -1
		for i, ele := range slice {
			if ele == element {
				index = i
			}
		}

		if index == -1 {
			return
		}

		*slicePtr = append(slice[:index], slice[index+1:]...)
	}
}

func RemoveAllFromSlice[T comparable](slicePtr *[]T, elements []T) {
	for _, element := range elements {
		RemoveFromSlice(slicePtr, element)
	}
}
